#ifndef BASE64_H
#define BASE64_H

#include <string>


namespace bzbstring {


const std::string BASE64_MIME_CHARS =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

/*
 * Encode a string in Base64 MIME format
 */

inline std::string base64_encode(const std::string &str,
								 const std::string characters = BASE64_MIME_CHARS) {

	std::string string_encoded;

	const size_t str_len = str.size();
	size_t index = 0;

	while (index < str_len) {

		uint8_t ch1 = str[index];
		uint8_t ch2 = ((index + 1) < str_len) ? str[index + 1] : 0;
		uint8_t ch3 = ((index + 2) < str_len) ? str[index + 2] : 0;

		uint8_t ch1_temp = (ch1 >> 0x02);
		uint8_t ch2_temp = ((ch1 & 0x03) << 0x04) | (ch2 >> 0x04);
		uint8_t ch3_temp = ((ch2 & 0x0F) << 0x02) | (ch3 >> 0x06);
		uint8_t ch4_temp = (ch3 & 0x3F);

		string_encoded += characters[ch1_temp];
		string_encoded += characters[ch2_temp];
		string_encoded += characters[ch3_temp];
		string_encoded += characters[ch4_temp];

		index += 3;
	}

	size_t padding;

	if (str_len % 3 == 0) {
		padding = 0;
	} else if (str_len % 3 == 1) {
		padding = 2;
	} else {
		padding = 1;
	}

	string_encoded.erase(string_encoded.end() - padding, string_encoded.end());
	while (padding--)
		string_encoded.append("=");

	return string_encoded;
}


/*
 * Decode a string in Base64 MIME format
 */

inline std::string base64_decode(const std::string &str,
								 const std::string characters = BASE64_MIME_CHARS) {

	unsigned int ch1_code;
	unsigned int ch2_code;
	unsigned int ch3_code;

	std::string string_decoded;

	const size_t str_len = str.size();
	size_t index = 0;

	while (index < str_len) {

		long int index1_temp = characters.find_first_of(str[index + 0]);
		long int index2_temp = characters.find_first_of(str[index + 1]);
		long int index3_temp = characters.find_first_of(str[index + 2]);
		long int index4_temp = characters.find_first_of(str[index + 3]);

		if(	index1_temp < -1 ||
			index2_temp < -1 ||
			index3_temp < -1 ||
			index4_temp < -1) {

			return "";
	}

		// First char code
		ch1_code = index1_temp << 0x02 | index2_temp >> 0x04;
		string_decoded += static_cast<char>(ch1_code);

		if (index3_temp & 0x80 || index4_temp & 0x80)
			break; // char not found (ie. '=', or not valid char)

		// Second char code
		ch2_code = (index2_temp << 0x04) | (index3_temp >> 0x02);
		string_decoded += static_cast<char>(ch2_code);

		// Third char code
		ch3_code = (index3_temp & 0x03) << 0x06 | (index4_temp & 0x3F);
		string_decoded += static_cast<char>(ch3_code);

		index += 4;
	}

	return string_decoded;
}


}

#endif